<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2021/2/19
 * Time: 16:53
 */

declare(strict_types=1);

namespace App\Service;
use App\Model\Dao\EventOrderDao;
use App\Model\Dao\MchAccountAccessDao;
use App\Model\Dao\MchAccountAccessDetailDao;
use App\Model\Dao\MchAccountDao;
use App\Utils\CustomUtils;
use Hyperf\DbConnection\Db;
use Hyperf\Di\Annotation\Inject;

/**
 * Class SettleEventOrderRechargeService
 * @package App\Service
 * 事件充值订单结算
 */
class SettleEventOrderRechargeService extends CommonService
{

    /**
     * @Inject()
     * @var MchAccountDao
     */
    private $MchAccountDao;

    /**
     * @Inject()
     * @var MchAccountAccessDao
     */
    private $MchAccountAccessDao;

    /**
     * @Inject()
     * @var EventOrderDao
     */
    private $EventOrderDao;

    /**
     * @Inject()
     * @var MchAccountAccessDetailDao
     */
    private $MchAccountAccessDetailDao;

    public function handle($param, $eventOrder, $mchAccount) {
        if ($eventOrder['settleStatus'] == 1) {
            return CustomUtils::xeServiceReturnData(SUCCESS_CODE, "事件充值订单已结算");
        }
        if ($param['handleStatus'] == 0) {
            return $this->handleAdd($param, $eventOrder, $mchAccount);
        } else {
            if ($param['handleStatus'] == 1) {
                return $this->handleFirstConfirm($eventOrder);
            } else {
                return $this->handleConfirm($param, $eventOrder, $mchAccount);
            }
        }
    }

    private function handleFirstConfirm($eventOrder) {
        $this->EventOrderDao->set($eventOrder['id'], [
            'settleStatus'  => 1,
        ]);
        return CustomUtils::xeServiceReturnData(SUCCESS_CODE, "事件充值订单初审通过结算成功");
    }

    private function handleAdd($param, $eventOrder, $mchAccount) {
        // 乐观锁版本号+1
        $updateVersion = $mchAccount['updateVersion'] + 1;
        $amount = $eventOrder['amount'];

        // 交易可提现金额入账 余额前后
        $beforeInTradeCashAmount = $mchAccount['tradeCashAmount'];
        $afterInTradeCashAmount  = $mchAccount['tradeCashAmount'] + $amount;

        // 交易可提现金额出账 余额前后
        $beforeOutTradeCashAmount = $afterInTradeCashAmount;
        $afterOutTradeCashAmount  = $afterInTradeCashAmount - $amount;

        // 事件待审核冻结金额 余额前后
        $beforeEventReviewFrozenAmount = $mchAccount['eventReviewFrozenAmount'];
        $afterEventReviewFrozenAmount = $mchAccount['eventReviewFrozenAmount'] + $amount;

        // 开启事务
        Db::beginTransaction();
        try {
            // step
            // 1.可提现入账 +100 cash in
            // 2.可提现出账 -100 cash out
            // 3.事件待审核入账 +100 frozen in
            // 4.更新事件订单
            // 5.更新账户
            $this->handleInCashEventAmount($param, $eventOrder, $mchAccount, $amount, $beforeInTradeCashAmount, $afterInTradeCashAmount);

            $this->handleOutCashEventAmount($param, $eventOrder, $mchAccount, $amount, $beforeOutTradeCashAmount, $afterOutTradeCashAmount);

            $this->handleInReviewEventAmount($param, $eventOrder, $mchAccount, $amount, $beforeEventReviewFrozenAmount, $afterEventReviewFrozenAmount);

            $this->EventOrderDao->update($eventOrder['id'], ['settleStatus' => 1]);

            $updateMchAccount = array(
                "tradeCashAmount"           => $afterOutTradeCashAmount,
                "eventReviewFrozenAmount"   => $afterEventReviewFrozenAmount,
                "updateVersion"             => $updateVersion
            );
            $result = $this->MchAccountDao->updateWhere([
                "id" => $param['mchAccountId'],
                "updateVersion" => $mchAccount['updateVersion']
            ], $updateMchAccount);

            // 如果更新账户失败，重试，最多3次
            if (!$result) {
                throw new \Exception("更新账户失败");
            }

        } catch (\Throwable $e) {
            CustomUtils::xzxDebug($e->getMessage());
            Db::rollBack();

            // 失败重试，最多3次
            $this->tryOneMore($param);

            return CustomUtils::xeServiceReturnData(FAILED_CODE, "事件充值单结算失败");
        }
        Db::commit();

        return CustomUtils::xeServiceReturnData(SUCCESS_CODE, "事件充值单结算成功");
    }

    private function handleConfirm($param, $eventOrder, $mchAccount) {
        // 乐观锁版本号+1
        $updateVersion = $mchAccount['updateVersion'] + 1;
        $amount = $eventOrder['amount'];

        // 待审冻结金额，解冻金额，余额前后
        $beforeFrozenReviewFrozenAmount = $mchAccount['eventReviewFrozenAmount'];
        $afterFrozenReviewFrozenAmount = $mchAccount['eventReviewFrozenAmount'] - $amount;

        // 交易可提现金额，出账，余额前后
        $beforeTradeCashAmount = $mchAccount['tradeCashAmount'];
        $afterTradeCashAmount  = $beforeTradeCashAmount + $amount;

        // 开启事务
        Db::beginTransaction();
        try {

            // step
            // 1.解冻事件待审冻结出账
            // 2.复审成功，交易可提现入账
            // 3.更新事件订单结算状态
            // 4.更新账户

            // step 1
            $accessId = $this->handleConfirmOutReviewEventAmount($param, $eventOrder, $mchAccount, $amount, $beforeFrozenReviewFrozenAmount, $afterFrozenReviewFrozenAmount);

            // handleAdd新增时添加的记录，查询
            $reviewInDetail = $this->MchAccountAccessDetailDao->getWhere(['eventOrderId'  => $eventOrder['id'], 'type' => TYPE_EVENT_REVIEW_IN]);

            $this->MchAccountAccessDetailDao->update($reviewInDetail['id'], ['accessId' => $accessId]);

            if ($param['handleStatus'] == 3) {
                // step 2
                $accessId = $this->handleConfirmInCashEventAmount($param, $eventOrder, $mchAccount, $amount, $beforeTradeCashAmount, $afterTradeCashAmount);

                // handleAdd新增时添加的记录，查询
                $cashInDetail = $this->MchAccountAccessDetailDao->getWhere(['eventOrderId'  => $eventOrder['id'], 'type' => TYPE_EVENT_CASH_IN]);

                $this->MchAccountAccessDetailDao->update($cashInDetail['id'], ['accessId' => $accessId]);

                $cashOutDetail = $this->MchAccountAccessDetailDao->getWhere(['eventOrderId'  => $eventOrder['id'], 'type' => TYPE_EVENT_CASH_OUT]);

                $this->MchAccountAccessDetailDao->update($cashOutDetail['id'], ['accessId' => $accessId]);
            }

            // step 3
            $this->EventOrderDao->set($eventOrder['id'], [
                'settleStatus'  => 1,
                'settleTime'    => time()
            ]);

            if ($param['handleStatus'] == 3) {
                $updateMchAccount = array(
                    "tradeCashAmount"           => $afterTradeCashAmount,
                    "eventReviewFrozenAmount"   => $afterFrozenReviewFrozenAmount,
                    "updateVersion"             => $updateVersion
                );
            } else {
                $updateMchAccount = array(
                    "eventReviewFrozenAmount"   => $afterFrozenReviewFrozenAmount,
                    "updateVersion"             => $updateVersion
                );
            }
            // step 4
            $result = $this->MchAccountDao->updateWhere([
                "id" => $param['mchAccountId'],
                "updateVersion" => $mchAccount['updateVersion']
            ], $updateMchAccount);

            // 如果更新账户失败，重试，最多3次
            if (!$result) {
                throw new \Exception("更新账户失败");
            }

        } catch (\Throwable $e) {
            CustomUtils::xzxDebug($e->getMessage());
            Db::rollBack();

            // 失败重试，最多3次
            $this->tryOneMore($param);

            return CustomUtils::xeServiceReturnData(FAILED_CODE, "事件充值单审核结算失败");
        }
        Db::commit();

        return CustomUtils::xeServiceReturnData(SUCCESS_CODE, "事件充值单审核结算成功");
    }

    /**
     * @param $param
     * @param $eventOrder
     * @param $mchAccount
     * @param $amount
     * @param $beforeAmount
     * @param $afterAmount
     * 可提现金额入账
     */
    private function handleInCashEventAmount($param, $eventOrder, $mchAccount, $amount, $beforeAmount, $afterAmount) {
        // 写明细表
        $mchAccountAccessDetail = array(
            "id"                => CustomUtils::createUUID(),
            "mchId"             => $eventOrder["mchId"],
            "mchNo"             => $eventOrder["mchNo"],
            "mchAccountId"      => $param['mchAccountId'],
            "accountType"       => ACCOUNT_TYPE_CASH,
            "inOutType"         => DETAIL_TYPE_IN,
            "type"              => TYPE_EVENT_CASH_IN,
            "beforeTotalAmount" => $beforeAmount,
            "afterTotalAmount"  => $afterAmount,
            "amount"            => $amount,
            "eventOrderId"      => $eventOrder['id'],
            "eventOrderNo"      => $eventOrder['eventOrderNo'],
            "currency"          => $eventOrder['currency'],
            "remark"            => "事件充值订单可提现账户入账",
            "createTime"        => CustomUtils::xzxGetMsecTime()
        );
        $this->MchAccountAccessDetailDao->add($mchAccountAccessDetail);
    }

    /**
     * @param $param
     * @param $eventOrder
     * @param $mchAccount
     * @param $amount
     * @param $beforeAmount
     * @param $afterAmount
     * 可提现金额出账
     */
    private function handleOutCashEventAmount($param, $eventOrder, $mchAccount, $amount, $beforeAmount, $afterAmount) {
        // 写明细表
        $mchAccountAccessDetail = array(
            "id"                => CustomUtils::createUUID(),
            "mchId"             => $eventOrder["mchId"],
            "mchNo"             => $eventOrder["mchNo"],
            "mchAccountId"      => $param['mchAccountId'],
            "accountType"       => ACCOUNT_TYPE_CASH,
            "inOutType"         => DETAIL_TYPE_OUT,
            "type"              => TYPE_EVENT_CASH_OUT,
            "beforeTotalAmount" => $beforeAmount,
            "afterTotalAmount"  => $afterAmount,
            "amount"            => -$amount,
            "eventOrderId"      => $eventOrder['id'],
            "eventOrderNo"      => $eventOrder['eventOrderNo'],
            "currency"          => $eventOrder['currency'],
            "remark"            => "事件充值订单可提现账户出账",
            "createTime"        => CustomUtils::xzxGetMsecTime()
        );
        $this->MchAccountAccessDetailDao->add($mchAccountAccessDetail);
    }

    /**
     * @param $param
     * @param $eventOrder
     * @param $mchAccount
     * @param $amount
     * @param $beforeAmount
     * @param $afterAmount
     * 事件待审核冻结金额入账
     */
    private function handleInReviewEventAmount($param, $eventOrder, $mchAccount, $amount, $beforeAmount, $afterAmount) {
        // 写明细表
        $mchAccountAccessDetail = array(
            "id"                => CustomUtils::createUUID(),
            "mchId"             => $eventOrder["mchId"],
            "mchNo"             => $eventOrder["mchNo"],
            "mchAccountId"      => $param['mchAccountId'],
            "accountType"       => ACCOUNT_TYPE_EVENT_REVIEW,
            "inOutType"         => DETAIL_TYPE_IN,
            "type"              => TYPE_EVENT_REVIEW_IN,
            "beforeTotalAmount" => $beforeAmount,
            "afterTotalAmount"  => $afterAmount,
            "amount"            => $amount,
            "eventOrderId"      => $eventOrder['id'],
            "eventOrderNo"      => $eventOrder['eventOrderNo'],
            "currency"          => $eventOrder['currency'],
            "remark"            => "事件充值订单待审核冻结账户入账",
            "createTime"        => CustomUtils::xzxGetMsecTime()
        );
        $this->MchAccountAccessDetailDao->add($mchAccountAccessDetail);
    }


    /**
     * @param $param
     * @param $eventOrder
     * @param $mchAccount
     * @param $amount
     * @param $beforeAmount
     * @param $afterAmount
     * @return string
     * 审核，事件充值审核金额出账
     */
    private function handleConfirmOutReviewEventAmount($param, $eventOrder, $mchAccount, $amount, $beforeAmount, $afterAmount) {
        // 写主表
        $mchAccountAccess = array(
            "id"           => CustomUtils::createUUID(),
            "mchId"        => $eventOrder["mchId"],
            "mchNo"        => $eventOrder["mchNo"],
            "mchAccountId" => $param['mchAccountId'],
            "accessType"   => ACCESS_TYPE_EVENT_ORDER_RECHARGE,
            "accountType"  => ACCOUNT_TYPE_EVENT_REVIEW,
            "eventOrderId" => $eventOrder['id'],
            "eventOrderNo" => $eventOrder['eventOrderNo'],
            "amount"       => 0,
            "currency"     => $eventOrder['currency'],
            "status"       => 1,
            "isShow"       => 0,
            "remark"       => "事件充值订单待审核冻结账户出账",
            "createTime"   => CustomUtils::xzxGetMsecTime()
        );
        $this->MchAccountAccessDao->add($mchAccountAccess);

        // 写明细表
        $mchAccountAccessDetail = array(
            "id"                => CustomUtils::createUUID(),
            "mchId"             => $eventOrder["mchId"],
            "mchNo"             => $eventOrder["mchNo"],
            "mchAccountId"      => $param['mchAccountId'],
            "accessId"          => $mchAccountAccess['id'],
            "accountType"       => ACCOUNT_TYPE_EVENT_REVIEW,
            "inOutType"         => DETAIL_TYPE_OUT,
            "type"              => TYPE_EVENT_CONFIRM_REVIEW_OUT,
            "beforeTotalAmount" => $beforeAmount,
            "afterTotalAmount"  => $afterAmount,
            "amount"            => -$amount,
            "eventOrderId"      => $eventOrder['id'],
            "eventOrderNo"      => $eventOrder['eventOrderNo'],
            "currency"          => $eventOrder['currency'],
            "remark"            => "事件充值订单待审核冻结账户出账",
            "createTime"        => CustomUtils::xzxGetMsecTime()
        );
        $this->MchAccountAccessDetailDao->add($mchAccountAccessDetail);
        return $mchAccountAccess['id'];
    }

    /**
     * @param $param
     * @param $eventOrder
     * @param $mchAccount
     * @param $amount
     * @param $beforeAmount
     * @param $afterAmount
     * @return string
     * 审核，可提现金额入账
     */
    private function handleConfirmInCashEventAmount($param, $eventOrder, $mchAccount, $amount, $beforeAmount, $afterAmount) {

        // 写主表
        $mchAccountAccess = array(
            "id"           => CustomUtils::createUUID(),
            "mchId"        => $eventOrder["mchId"],
            "mchNo"        => $eventOrder["mchNo"],
            "mchAccountId" => $param['mchAccountId'],
            "accessType"   => ACCESS_TYPE_EVENT_ORDER_RECHARGE,
            "accountType"  => ACCOUNT_TYPE_CASH,
            "eventOrderId" => $eventOrder['id'],
            "eventOrderNo" => $eventOrder['eventOrderNo'],
            "amount"       => $amount,
            "currency"     => $eventOrder['currency'],
            "status"       => 1,
            "remark"       => "事件充值订单入账",
            "createTime"   => CustomUtils::xzxGetMsecTime()
        );
        $this->MchAccountAccessDao->add($mchAccountAccess);

        // 写明细表
        $mchAccountAccessDetail = array(
            "id"                => CustomUtils::createUUID(),
            "mchId"             => $eventOrder["mchId"],
            "mchNo"             => $eventOrder["mchNo"],
            "mchAccountId"      => $param['mchAccountId'],
            "accessId"          => $mchAccountAccess['id'],
            "accountType"       => ACCOUNT_TYPE_CASH,
            "inOutType"         => DETAIL_TYPE_IN,
            "type"              => TYPE_EVENT_CONFIRM_CASH_IN,
            "beforeTotalAmount" => $beforeAmount,
            "afterTotalAmount"  => $afterAmount,
            "amount"            => $amount,
            "eventOrderId"      => $eventOrder['id'],
            "eventOrderNo"      => $eventOrder['eventOrderNo'],
            "currency"          => $eventOrder['currency'],
            "remark"            => "事件充值订单入账",
            "createTime"        => CustomUtils::xzxGetMsecTime()
        );
        $this->MchAccountAccessDetailDao->add($mchAccountAccessDetail);
        return $mchAccountAccess['id'];
    }

    /**
     * @param $param
     * @param $eventOrder
     * @param $mchAccount
     * @param $amount
     * @param $beforeAmount
     * @param $afterAmount
     * 冻结审核，事件充值金额入账
     */
    private function handleConfirmInFrozenEventAmount($param, $eventOrder, $mchAccount, $amount, $beforeAmount, $afterAmount) {

        // 写主表
        $mchAccountAccess = array(
            "id"           => CustomUtils::createUUID(),
            "mchId"        => $eventOrder["mchId"],
            "mchNo"        => $eventOrder["mchNo"],
            "mchAccountId" => $param['mchAccountId'],
            "accessType"   => ACCESS_TYPE_EVENT_ORDER_FROZEN,
            "accountType"  => ACCOUNT_TYPE_EVENT_FROZEN,
            "eventOrderId" => $eventOrder['id'],
            "eventOrderNo" => $eventOrder['eventOrderNo'],
            "amount"       => $amount,
            "currency"     => $eventOrder['currency'],
            "status"       => 1,
            "remark"       => "事件充值订单入账",
            "createTime"   => CustomUtils::xzxGetMsecTime()
        );
        $this->MchAccountAccessDao->add($mchAccountAccess);

        // 写明细表
        $mchAccountAccessDetail = array(
            "id"                => CustomUtils::createUUID(),
            "mchId"             => $eventOrder["mchId"],
            "mchNo"             => $eventOrder["mchNo"],
            "mchAccountId"      => $param['mchAccountId'],
            "accessId"          => $mchAccountAccess['id'],
            "inOutType"         => DETAIL_TYPE_IN,
            "type"              => TYPE_EVENT_CONFIRM_FROZEN_IN,
            "beforeTotalAmount" => $beforeAmount,
            "afterTotalAmount"  => $afterAmount,
            "amount"            => $amount,
            "eventOrderId"      => $eventOrder['id'],
            "eventOrderNo"      => $eventOrder['eventOrderNo'],
            "currency"          => $eventOrder['currency'],
            "remark"            => "事件充值订单入账",
            "createTime"        => CustomUtils::xzxGetMsecTime()
        );
        $this->MchAccountAccessDetailDao->add($mchAccountAccessDetail);
    }

}